﻿using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Collections.Generic;
using NHibernate;
using NHibernate.Criterion;

namespace NMA.Infrastructure.NHibernate.Repository
{
    using NMA.Domain.Model;
    using NMA.Infrastructure.NHibernate.UnitOfWork;
    using NMA.Infrastructure.NHibernate.DomainObject;    
    using NMA.Infrastructure.NHibernate.Container;    
    using NMA.Domain.Model.Repository;
    using NMA.Domain.Shared.Paging;    

    public class NewsRepository : BaseRepository<INews>, INewsRepository
    {
        #region variables

        protected IUnitOfWorkFactory UnitOfWorkFactory { get; private set; }        

        #endregion

        #region contructors

        public NewsRepository() : this(IoC.GetInstance<IUnitOfWorkFactory>())
        { }

        public NewsRepository(IUnitOfWorkFactory unitOfWorkFactory)
        {
            UnitOfWorkFactory = unitOfWorkFactory;
        }

        #endregion

        #region implement functions       

        public IEnumerable<INews> All()
        {            
            using (var uow = UnitOfWorkFactory.GetNewUnitOfWork())
            {
                ICriteria critreria = uow.Session.CreateCriteria<News>();
                uow.Commit();
                return critreria.List<News>();
            }
        }

        public void Remove(Guid id)
        {            
            var entity = GetById(id);

            if (entity == null)
                throw new Exception("News's entity is null");                

            using (var uow = UnitOfWorkFactory.GetNewUnitOfWork())
            {
                uow.Session.Delete(entity);
                uow.Commit();
            }           
        }

        public INews GetById(Guid id)
        {            
            INews news = null;

            using (var uow = UnitOfWorkFactory.GetNewUnitOfWork())
            {
                news = uow.Session.Get<News>(id);
                uow.Commit();
            }

            return news;                     
        }
        
        public Guid Add(INews entity)
        {            
            object result = null;

            using (var uow = UnitOfWorkFactory.GetNewUnitOfWork())
            {
                result = uow.Session.Save(entity);

                uow.Session.Flush();
                uow.Session.Clear();

                uow.Commit();
            }

            return (Guid)result;            
        }

        public void Remove(INews entity)
        {            
            using (var uow = UnitOfWorkFactory.GetNewUnitOfWork())
            {
                uow.Session.Delete(entity);
                uow.Commit();
            }            
        }

        public void Update(INews entity)
        {            
            using (var uow = UnitOfWorkFactory.GetNewUnitOfWork())
            {
                uow.Session.Update(entity);
                uow.Commit();
            }            
        }

        public IEnumerable<INews> GetBy(Expression<Func<INews, bool>> condition)
        {            
            IEnumerable<INews> result = null;
            IList<INews> listOfNews = null;

            using (var uow = UnitOfWorkFactory.GetNewUnitOfWork())
            {
                // creating criteria for this condition
                ICriteria criteria = uow.Session.CreateCriteria(typeof(INews));
                // getting result from criteria
                listOfNews = criteria.List<INews>();

                if (listOfNews == null)
                    throw new Exception("List of news is null");                    

                result = listOfNews.Where<INews>(condition.Compile());
            }

            return result;            
        }

        public int CountAllNews()
        {            
            int result = 0;

            using (var uow = UnitOfWorkFactory.GetNewUnitOfWork())
            {
                // create criteria for this condition
                ICriteria criteria = uow.Session.CreateCriteria(typeof(INews));
                // set projection to row count
                criteria.SetProjection(Projections.RowCount());
                // get result from criteria
                result = criteria.UniqueResult<int>();
                uow.Commit();
            }

            return result;            
        }

        public int GetNumberOfView(Guid id)
        {            
            var entity = GetById(id);

            if (entity == null)
                throw new Exception("News's object is null");                

            return entity.NumberOfView;            
        }

        public IPagedList<INews> All(int pageIndex, int pageSize)
        {
            using (var uow = UnitOfWorkFactory.GetNewUnitOfWork())
            {
                var criteria = uow.Session.CreateCriteria(typeof(News));

                return PagedList(criteria, pageIndex, pageSize);
            }
        }

        #endregion        
    }
}
